<!DOCTYPE html>
<html lang='en'>

<head>
    <meta charset='UTF-8'>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <meta http-equiv='X-UA-Compatible' content='ie=edge'>
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            list-style: none;
        }

        body {
            width: 100%;
            height: 100%;
            background: #000;
        }

        .container {
            text-align: center;
            margin: 200px auto;
        }

        li {
            position: relative;
            display: inline-block;
            width: 55px;
            height: 200px;
            background-color: #fff;
            border-radius: 5px;
            cursor: pointer;
            font-size: 16px;
            box-shadow: 0 0 5px 3px rgba(0, 0, 0, 0.5);
        }

        li span {
            position: absolute;
            width: 100%;
            left: 0;
            bottom: 20px;
        }

        .low::after {
            content: '.';
            position: absolute;
            left: 0;
            bottom: 10px;
            width: 100%;
        }

        .high::after {
            content: '.';
            position: absolute;
            left: 0;
            bottom: 45px;
            width: 100%;
        }
    </style>
</head>

<body>
    <ul class="container">
        <li key='1' class="low"><span>1</span></li>
        <li key='2' class="low"><span>2</span></li>
        <li key='3' class="low"><span>3</span></li>
        <li key='4' class="low"><span>4</span></li>
        <li key='5' class="low"><span>5</span></li>
        <li key='6' class="low"><span>6</span></li>
        <li key='7' class="low"><span>7</span></li>
        <li key='q'><span>1</span></li>
        <li key="w"><span>2</span></li>
        <li key='e'><span>3</span></li>
        <li key='r'><span>4</span></li>
        <li key='t'><span>5</span></li>
        <li key='y'><span>6</span></li>
        <li key='u'><span>7</span></li>
        <li key='a' class="high"><span>1</span></li>
        <li key='s' class="high"><span>2</span></li>
        <li key='d' class="high"><span>3</span></li>
        <li key='f' class="high"><span>4</span></li>
        <li key='g' class="high"><span>5</span></li>
        <li key='h' class="high"><span>6</span></li>
        <li key='j' class="high"><span>7</span></li>
    </ul>
</body>

<hmtl>
    <script>
        var b = true
        //钢琴样式改变函数
        function changeColor(li) {
            li.style.background = ' #CCC';
            setTimeout(function () {
                    li.style.background = ' #fff';
                },
                100)
        }
        var container = document.querySelector('.container');
        //函数控制钢琴键点击事件钢琴键样式的改变并发出音阶
        container.onclick = function (e) {
            //tagName 属性返回元素的标签名HTML 返回 tagName 属性的值是大写的
            if (e.target.tagName === 'UL') {
                return; //如果点击的是ul则不执行下面代码
            }
            var li = e.target; //用一个变量表示被点击的元素
            //点击到SPAN要控制父级变色，事件委托
            if (e.target.tagName == 'SPAN') {
                li = e.target.parentNode
            }
            changeColor(li); //改变被点击的钢琴键样式
            //获得钢琴是第几键，用变量接受当前被点击的li的索引值
            var index = getIndex(li);
            // console.log(getIndex(li))
            makeSound(index); //将音频数组中对应索引的音频值转化为声音输出
        }

        window.onkeydown = function (e) {
            if (b) {
                b = false
                //属性选择器
                var li = document.querySelector("li[key=\"" + e.key + "\"]");
                if (li) {
                    this.changeColor(li); //改变触发事件的钢琴键样式
                    var index = getIndex(li); //获得钢琴是第几键，用变量接受当前被点击的li的索引值
                    makeSound(index); //将音频数组中对应索引的音频值转化为声音输出
                }
            }
        }
        window.onkeyup = function () {
            b = true
        }

        function getIndex(li) {
            var ul = li.parentNode;
            //Array. from ( 类数组)返回真数组
            //获取到ul下的所有子级元素节点集合，并转换为真数组
            var children = Array.from(ul.children)
            return children.indexOf(li);
        }

        function makeSound(index) { //钢琴传入是钢琴的第几个按键
            var sounds = [130, 147, 165, 175, 196, 220, 246, 262, 294, 330, 349, 392, 440, 494,
                523, 587, 659, 698, 784, 880, 988, 1047
            ] //钢琴键音音频数组
            var ctx
            setContext(); //获得音频上下文
            var osc = ctx.createOscillator() //获得音频振荡器
            var g = ctx.createGain() //获得音量控制器
            osc.connect(g) //连接音量控制器
            osc.type = " sine " //设置波形
            osc.frequency.value = sounds[index] //对应钢琴不同键的不同频率
            var duration = 1 //控制时间
            g.connect(ctx.destination) //连接扬声器
            g.gain.value = 0 //初始音高为0
            osc.start(); //从当前开始发生
            g.gain.linearRampToValueAtTime(.6, ctx.currentTime + 0.01) //从当前时间到.01s后,音高从0~0. 6
            osc.stop(ctx.currentTime + duration); //当前时间duration后停止
            g.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + duration) //0.6~0.01

            function setContext() {
                if (!ctx) { //创建音频上下文实例
                    ctx = new AudioContext()
                }
            }
        }
    </script>
    <!-- 
    定义和用法
:after 选择器在被选元素的内容后面插入内容。
请使用 content 属性来指定要插入的内容 -->

    <!-- 
    类数组是指在写法上跟数组一样，
例如 arguments 和 length 写法上跟数组一样，
不具有数组所具有的方法
但类数组，只是一个普通的对象，他的原型是Object。
而真实的数组是Array类型 -->